package fsclient

import (
	"errors"
	"net"
	"sync"
	"time"

	"github.com/astaxie/beego"
	"i-hea.com/ttsc/ttsclient"
)

type clientManger struct {
	clients      map[int]*TcpClientModel
	serverAddr   string
	clientNumber int
}

var cliManger clientManger

type ttsTask struct {
	Session  uint32
	FileType uint32
	FileName string
	TtsText  string
	Create   time.Time
}

type ttsResult struct {
	sessionId int32
	result    int32
	err       error
}

type TcpClientModel struct {
	taskId      int
	shutdown    bool
	conn        net.Conn
	taskMap     map[uint32]*ttsTask
	connLock    *sync.Mutex
	taskLock    *sync.RWMutex
	resultChan  chan (*ttsResult)
	newTaskChan chan (*ttsTask)
}

func (c *TcpClientModel) responseResult(t *ttsResult) {

	if t.err != nil {
		beego.Error(t.sessionId, "tts convert wrong:", t.err)
	}

	data, err := resultEncode(t.sessionId, t.result)
	if err != nil {
		beego.Error(t.sessionId, "tts encode failed:", err)
	}

	beego.Debug("send result:", t.sessionId, t.result)
	//c.connLock.Lock()
	//defer c.connLock.Unlock()
	n, err := c.conn.Write(data.Bytes())
	if err != nil {
		beego.Error(t.sessionId, "tts write failed:", err)
		c.destroy()
	}

	beego.Debug(t.sessionId, "tts convert result:", t.result, "send:", n)
}

func (c *TcpClientModel) resultProcessRoutine() {

	beego.Debug("result process routine standby...")
	for {

		if c.shutdown {
			beego.Debug("connection is shutdown , close")
			return
		}
		select {

		case result, ok := <-c.resultChan:
			beego.Debug("new result arrive:", result)
			if ok {

				c.responseResult(result)
			} else {
				beego.Debug("result channel is closed")
				break
			}
		}
	}
}

func (c *TcpClientModel) destroy() {
	c.shutdown = true
}

func (c *TcpClientModel) taskToSrv(tts *ttsTask) {

	response := ttsResult{
		sessionId: int32(tts.Session),
	}

	_, err := ttsclient.NewTask(tts.FileName, tts.TtsText)

	if err != nil {
		beego.Error("tts task failed:", err, tts.Session, "tts string", tts.TtsText)
		response.err = err
		response.result = -1
		c.resultChan <- &response

	} else {
		beego.Debug("tts task success:", tts.Session, "tts string", tts.TtsText)
		response.err = nil
		response.result = 0
		c.resultChan <- &response
	}

	c.taskLock.Lock()
	delete(c.taskMap, tts.Session)
	c.taskLock.Unlock()

	return

}

func (c *TcpClientModel) doTaskRoutine() {

	beego.Debug("do routine standby...")
	for {

		if c.shutdown {
			beego.Debug("detect shutdown ,quit")
			break
		}
		select {
		case task, ok := <-c.newTaskChan:
			if ok {

				c.taskToSrv(task)
			} else {
				beego.Info("DotskRoutine quit...")
				break
			}
		}
	}
}

func (c *TcpClientModel) mainLoopRoutine() {
	beego.Debug("main loop routine standby...")
	for {
		if c.shutdown {
			c.conn.Close()
			time.Sleep(1 * time.Minute)

			close(c.newTaskChan)
			close(c.resultChan)
			break
		} else {
			time.Sleep(1 * time.Minute)
		}
	}
}

func (c *TcpClientModel) addNewTtsTask(task *ttsTask) error {

	if len(c.newTaskChan) > 1000 {
		beego.Error("task chan is full,drop task", task.Session, task.TtsText)
		return errors.New("task chan is full")
	} else {

		c.newTaskChan <- task

		c.taskLock.Lock()
		defer c.taskLock.Unlock()
		task.Create = time.Now()
		c.taskMap[task.Session] = task

		return nil
	}
}

func (c *TcpClientModel) recvRoutine() {

	beego.Debug("recv routine standby...")
	for {

		if c.shutdown {
			beego.Debug("connection is shutdown , close")
			return
		}

		newTask, err := protoDecodeTask(c.conn)

		if err != nil {
			beego.Error("error decoding message ", err, ".destroy!")
			c.destroy()
			break
		}

		c.addNewTtsTask(newTask)
	}
}

//连接服务器
func connectServers(servAddr string) (*TcpClientModel, error) {

	conn, err := net.Dial("tcp", servAddr)
	if err != nil {
		beego.Error(err)
		return nil, err
	}

	//defer conn.Close()
	tcpClientM := &TcpClientModel{
		shutdown:    false,
		conn:        conn,
		resultChan:  make(chan *ttsResult, 1000),
		newTaskChan: make(chan *ttsTask, 1000),
		taskMap:     make(map[uint32]*ttsTask),
		connLock:    new(sync.Mutex),
		taskLock:    new(sync.RWMutex),
	}

	//conn.SetWriteDeadline(time.Now().Add(time.Duration(5 * time.Second)))

	go tcpClientM.mainLoopRoutine()
	go tcpClientM.recvRoutine()
	go tcpClientM.resultProcessRoutine()
	go tcpClientM.doTaskRoutine()

	return tcpClientM, nil
}

func (p *clientManger) managerConnection() {

	for {
		for k, v := range p.clients {
			if v.shutdown {
				beego.Debug("client ", k, "is disconnected")
				client, err := connectServers(p.serverAddr)
				if err == nil {
					p.clients[k] = client
				}
			}
		}

		time.Sleep(10 * time.Second)
	}
}

func StartNewClient(servAddr string, clientNumber int) {

	cliManger.clientNumber = clientNumber
	cliManger.serverAddr = servAddr

	cliManger.clients = make(map[int]*TcpClientModel)

	for i := 0; i < clientNumber; i++ {
		client, err := connectServers(servAddr)
		if err != nil {
			beego.Error("create new clinet to freeswitch failed,retry after 1 minute!")
			time.Sleep(1 * time.Minute)
			continue
		}

		client.taskId = i
		cliManger.clients[i] = client

		beego.Debug(i, "Connect to server success:", client.conn.LocalAddr())
	}

	go cliManger.managerConnection()

	return
}
